package com.evolved.automata.experimental;
import java.io.*;
import java.util.*;


import com.evolved.automata.*;

public class BehaviorDistribution implements Serializer {
	
	public static class InferenceStrategy
	{
		public InferenceMethod j_InferenceMethod;
		public double[] j_InferenceFreedom;
		public double j_InferenceFreedomFraction;
		
		public InferenceStrategy(InferenceMethod method, double fraction)
		{
			j_InferenceMethod=method;
			j_InferenceFreedomFraction=fraction;
		}
		
		public InferenceStrategy(InferenceMethod method, double[] freedom)
		{
			j_InferenceMethod=method;
			j_InferenceFreedom=freedom;
		}
		
	}
	
	public double[] j_BaseInferentialFreedom;

	
	LinkedList[] j_StateActionDistribution;
	int[] j_StateComponentRadii;
	int[] j_AllocatedStateList;
	int[][] j_AllocatedStateSet;
	int j_NumStatesAllocated;
	int j_AllocationBufferSize;
	StateValueDistribution[] j_ValueDistribution;
	
	
	private int j_RadiiLength;
	private int j_RadiiIndex;
	private int j_DistributionIndex;
	private int j_DistributionLength;
	private static final double j_DistanceMismatchTerm=1000;
	
	// Inference related fields
	public enum InferenceMethod
	{
		NONE,
		BAYESIAN,
		NEIGHBORHOOD,
		NEIGHBORHOOD_10,
		NEIGHBORHOOD_25,
		NEIGHBORHOOD_33,
		NEIGHBORHOOD_50,
		NEIGHBORHOOD_100,
		CLUSTER
		
	}
	
	private ExtinctionPolicy j_ExtinctionPolicy;
	
	// end of inference fields
	
	/*
	 * BEGIN OF Inner Class
	 */
	public interface ExtinctionPolicy
	{
		public LinkedList<WeightedValue<Integer>> TransformPoint(LinkedList<WeightedValue<Integer>> baseDistribution);
	}
	
	/*
	 * END OF Inner Class
	 * 
	 */
	
	// this is the constructor for reloading from files
	public BehaviorDistribution()
	{
		j_SerializerHelper= new SerializerAdapter(this);
		j_CurrentState=LoadState.READING_RADII_SIZE;
	}
	

	public BehaviorDistribution(LinkedList[] stateDistribution, int[] stateVectorRadii, int[][] allocationSet)
	{
		baseInitialization(stateVectorRadii);
		
		j_StateActionDistribution = new LinkedList[stateDistribution.length];
		WeightedValue<Integer> actionWeight;
		j_AllocatedStateList = new int[stateDistribution.length];
		j_AllocatedStateSet = (int[][])allocationSet.clone();
		
		for (int i=0;i<stateDistribution.length;i++)
		{
			if (stateDistribution[i]!=null)
			{
				for (int j=0;j<stateDistribution[i].size();j++)
				{
					actionWeight = (WeightedValue<Integer>)stateDistribution[i].get(j);
					j_StateActionDistribution[i].add(new WeightedValue<Integer>(actionWeight.GetValue(), actionWeight.GetWeight()));
				}
				
				j_AllocatedStateList[j_NumStatesAllocated]=j_AllocatedStateSet[i][0]; // logical state
				
				j_NumStatesAllocated++;
				
			}
			
		}
		for (int i=j_NumStatesAllocated;i<stateDistribution.length;i++)
			j_AllocatedStateList[i]=-1;
	}
	
	// this is the constructor for when the BehaviorDistribution needs to be initialized for loading for the first
	// time.  The assumption is that the action is increment
	public BehaviorDistribution(int[] stateVectorRadii, double initialWeight, int totalActions, int allocationBuffer)
	{
		
		baseInitialization(stateVectorRadii);
		
		initializeAllocationSet(allocationBuffer);
		
		for (int i=0;i<allocationBuffer;i++)
		{
			j_AllocatedStateSet[i][0]=-1;
			j_AllocatedStateSet[i][1]=-1;
			j_AllocatedStateList[i]=-1;
			j_StateActionDistribution[i] = new LinkedList<LinkedList<WeightedValue<Integer>>>();
			for (int j=0;j<totalActions;j++)
			{
				j_StateActionDistribution[i].add(new WeightedValue<Integer>(new Integer(j), initialWeight));
			}
		}
	}
	
	private void baseInitialization(int[] stateVectorRadii)
	{
		j_CurrentState=LoadState.READING_RADII_SIZE;
		j_NumStatesAllocated=0;
		j_SerializerHelper= new SerializerAdapter(this);
		j_ValueDistribution = new StateValueDistribution[stateVectorRadii.length];
		
		j_StateComponentRadii= (int[])stateVectorRadii.clone();
	}
	
	private void initializeAllocationSet(int allocationBufferSize)
	{
		j_AllocationBufferSize=allocationBufferSize;
		j_StateActionDistribution= new LinkedList[allocationBufferSize];
		j_AllocatedStateList = new int[allocationBufferSize];
		j_AllocatedStateSet = new int[allocationBufferSize][2];
	}

	// this is the constructor for when the BehaviorDistribution needs to be initialized for loading for the first
	// time.  The assumption is that the action is increment
	public BehaviorDistribution(int[] stateVectorRadii, LinkedList<WeightedValue<Integer>> initialActions, int allocationBuffer)
	{
		
		baseInitialization(stateVectorRadii);
		initializeAllocationSet(allocationBuffer);
		
		for (int i=0;i<allocationBuffer;i++)
		{
			j_AllocatedStateSet[i][0]=-1;
			j_AllocatedStateSet[i][1]=-1;
			j_AllocatedStateList[i]=-1;
			j_StateActionDistribution[i] = new LinkedList<LinkedList<WeightedValue<Integer>>>();
			for (int j=0;j<initialActions.size();j++)
			{
				j_StateActionDistribution[i].add(new WeightedValue<Integer>(initialActions.get(j).GetValue(), initialActions.get(j).GetWeight()));
			}
		}
	}
	
	
	
	public BehaviorDistribution(int[] stateVectorRadii, int[][] allocationSet)
	{
		j_ValueDistribution = new StateValueDistribution[stateVectorRadii.length];
		j_CurrentState=LoadState.READING_RADII_SIZE;
		j_SerializerHelper= new SerializerAdapter(this);
		j_StateComponentRadii= new int[stateVectorRadii.length];
		for (int i=0;i<stateVectorRadii.length;i++)
			j_StateComponentRadii[i]=stateVectorRadii[i];
		
		
		j_AllocationBufferSize=allocationSet.length;
		j_AllocatedStateSet = new int[j_AllocationBufferSize][2];
		j_NumStatesAllocated=0;
		j_AllocatedStateList = new int[j_AllocationBufferSize];
		for (int i=0;i<j_AllocationBufferSize;i++)
		{
			j_AllocatedStateSet[i][0]=allocationSet[i][0];
			j_AllocatedStateSet[i][1]=allocationSet[i][1];
			if (j_AllocatedStateSet[i][0]!=-1)
				j_AllocatedStateList[j_NumStatesAllocated++]=j_AllocatedStateSet[i][0];
		}
		for (int i=j_NumStatesAllocated;i<j_AllocationBufferSize;i++)
			j_AllocatedStateList[i]=-1;
		j_StateActionDistribution= new LinkedList[j_AllocationBufferSize];
		
		
		
	}
	
	
	
	private void updateStateValueDistribution()
	{
		for (int i=0;i<j_ValueDistribution.length;i++)
			j_ValueDistribution[i] = new StateValueDistribution(j_BaseInferentialFreedom[i]*j_StateComponentRadii[i]);
		int[] mappedState;
		for (int i=0;i<j_NumStatesAllocated;i++)
		{
			mappedState = AITools.ReverseMapIndexToVector(j_AllocatedStateList[i], j_StateComponentRadii);
			for (int j=0;j<j_StateComponentRadii.length;j++)
			{
				j_ValueDistribution[j].addValue(mappedState[j]);
			}
		}
	}
	public void SetExtinctionPolicy(ExtinctionPolicy policy)
	{
		j_ExtinctionPolicy = policy;
	}
	
	public int[][] GetAllocationSet()
	{
		return j_AllocatedStateSet;
	}
	
	public int[] GetStateRadii(){
		return j_StateComponentRadii;
	}
	

	public Object clone()
	{
		LinkedList[] newBehavior = new LinkedList[j_StateActionDistribution.length];
		LinkedList<WeightedValue<Integer>> newValue;
		int i=0;
		for (LinkedList<WeightedValue<Integer>> oldValue:j_StateActionDistribution)
		{
			newValue=(LinkedList<WeightedValue<Integer>>)oldValue.clone();
			newBehavior[i]=newValue;
			i++;
		}
		
		int[] newRadii = (int[])j_StateComponentRadii.clone();
		BehaviorDistribution newDist = new BehaviorDistribution(newBehavior,newRadii,j_AllocatedStateSet);
		newDist.j_BaseInferentialFreedom=(double[])j_BaseInferentialFreedom.clone();
		return newDist;
	}
	
	public BehaviorDistribution MergeClone(BehaviorDistribution mergeDistrib)
	{
		LinkedList[] mergeDistribution = mergeDistrib.j_StateActionDistribution;
		LinkedList[] newBehavior = new LinkedList[j_StateActionDistribution.length];
		LinkedList<WeightedValue<Integer>> newValue,oldValue1, mergeValue;
		
		for (int i=0;i<j_StateActionDistribution.length;i++)
		{
			oldValue1 = (LinkedList<WeightedValue<Integer>>)j_StateActionDistribution[i];
			mergeValue = (LinkedList<WeightedValue<Integer>>)mergeDistribution[i];
			newValue=AITools.MergeLists(oldValue1, mergeValue);
			newBehavior[i]=newValue;
		}
		
		int[] newRadii = (int[])j_StateComponentRadii.clone();
		BehaviorDistribution newDist = new BehaviorDistribution(newBehavior,newRadii,j_AllocatedStateSet);
		return newDist;
	}
	

	public BehaviorDistribution MergeClone(BehaviorDistribution mergeDistrib, LinkedList<WeightedValue<Integer>> skewValue)
	{
		LinkedList[] mergeDistribution = mergeDistrib.j_StateActionDistribution;
		LinkedList[] newBehavior = new LinkedList[j_StateActionDistribution.length];
		LinkedList<WeightedValue<Integer>> newValue,oldValue1, mergeValue;
		
		for (int i=0;i<j_StateActionDistribution.length;i++)
		{
			oldValue1 = (LinkedList<WeightedValue<Integer>>)j_StateActionDistribution[i];
			mergeValue = (LinkedList<WeightedValue<Integer>>)mergeDistribution[i];
			newValue=AITools.MergeLists(oldValue1, mergeValue);
			newValue = AITools.MergeLists(newValue, skewValue);
			newBehavior[i]=newValue;
			
		}
		
		int[] newRadii = (int[])j_StateComponentRadii.clone();
		BehaviorDistribution newDist = new BehaviorDistribution(newBehavior,newRadii,j_AllocatedStateSet);
		return newDist;
	}
	
	
	
	public int MapIndexRaw(int[] stateComponents)
	{
		return AITools.MapDiscreteVectorToInt(stateComponents, j_StateComponentRadii);
	}
	
	/*
	public void SetMaxClusterFraction(double[] clusterFraction)
	{
		j_BaseInferentialFreedom=clusterFraction;
	}
	*/
	public void SetBaseInferentialFreedom(double[] infFreedom)
	{
		j_BaseInferentialFreedom=infFreedom;
		updateStateValueDistribution();
	}
	
	
	
	public boolean CheckInferenceCluster(int[] stateComponents, double cluster_size_fraction)
	{
		int[] allocatedState;
		LinkedList<WeightedValue<Integer>> chosenValues, distances = new LinkedList<WeightedValue<Integer>>();
		WeightedValue<Integer> distanceWeight;
		LinkedList<Integer> mappedIndices = new LinkedList<Integer>();
		double dist;
		for (int i=0;i<j_NumStatesAllocated;i++)
		{
			allocatedState = AITools.ReverseMapIndexToVector(j_AllocatedStateList[i], j_StateComponentRadii);
			dist=distanceThreshold(allocatedState,stateComponents);
			if (dist>=0)
			{
				distanceWeight = new WeightedValue<Integer>(i,dist);
				distances.add(distanceWeight);
			}
		}
		
		
		chosenValues =AITools.ChooseWeightedRandomPartition(distances,false);
		
		for (WeightedValue<Integer> v:chosenValues )
		{
			mappedIndices.add(v.GetValue());
		}
		
		if (mappedIndices.size()<=j_NumStatesAllocated*cluster_size_fraction)
			return true;
		else
			return false;
	}
	
	

	

	
	public int GetAllocationIndex(int baseIndex, boolean ifNotExistsCreateP)
	{
		return AITools.HashMapDiscreteVector( baseIndex,j_AllocatedStateSet,j_StateActionDistribution,ifNotExistsCreateP);
	}
	
	public int GetAllocationIndex(int[] stateComponents, boolean ifNotExistsCreateP)
	{
		return AITools.HashMapDiscreteVector( AITools.MapDiscreteVectorToInt(stateComponents, j_StateComponentRadii),j_AllocatedStateSet,j_StateActionDistribution,ifNotExistsCreateP);
	}
	
	
	
	
	
	
	public LinkedList<Integer> MapIndex(int[] stateComponents, InferenceStrategy strategy)
	{
		
		int mappedIndex;
		boolean exactMatchRequired=true;
		int stateLength = stateComponents.length;
		
		if (strategy.j_InferenceMethod==InferenceMethod.CLUSTER)
		{
			boolean missingComponentsP=false;
			for (int i=0;i<stateLength;i++)
			{
				if (stateComponents[i]==-1)
				{
					missingComponentsP=true;
					break;
				}
			}
			if (missingComponentsP)
				strategy.j_InferenceMethod=InferenceMethod.BAYESIAN;
			else
				return MapIndexFromAllocated(stateComponents,j_DistanceMismatchTerm);
			
		}
		
			
		
		double[] effectiveFreedomFractions = GetEffectiveInferentialFreedom(stateComponents,strategy);
		
		int variantLength=0;
		for (int i=0;i<stateLength;i++)
			if (effectiveFreedomFractions[i]!=0)
			{
				exactMatchRequired= false;
				variantLength++;
			}
		
		LinkedList<Integer> mappedIndices=null;
		if (exactMatchRequired)
		{
			mappedIndex =  GetAllocationIndex(stateComponents, false);
			if (mappedIndex!=j_AllocationBufferSize)
			{
				mappedIndices = new LinkedList<Integer>();
				mappedIndices.add(new Integer(mappedIndex));
			}
			return mappedIndices;
		}
		if (GetStateEntropy(stateComponents,effectiveFreedomFractions)>j_NumStatesAllocated)
			return MapIndexFromAllocated(stateComponents,effectiveFreedomFractions);
		else
			return MapIndexFromEntropy(stateComponents,effectiveFreedomFractions,variantLength);
	}
	

	
	public void WriteDetailedBehaviorsToFile(String filefullname)
	{
		BufferedWriter bWriter=null;
		File f = new File(filefullname);
		int[] allocatedState;
		LinkedList<WeightedValue<Integer>> mappedValues;
		int mappedIndex; 
		String stateString, actionString, formattedString = "%1$s  -->  %2$s",outputString;
		int[][] actionDetails;
		try
		{
			if (f.exists())
				f.delete();
			bWriter = new BufferedWriter(new FileWriter(filefullname));
			for (int i=0;i<j_NumStatesAllocated;i++)
			{
				allocatedState = AITools.ReverseMapIndexToVector(j_AllocatedStateList[i], j_StateComponentRadii);
				mappedIndex = GetAllocationIndex(j_AllocatedStateList[i], false);
				mappedValues = j_StateActionDistribution[mappedIndex];
				actionDetails=AITools.DistribViewer(mappedValues);
				stateString=StaticTools.WriteArrayString(allocatedState, " ");
				actionString = StaticTools.WriteArrayAsStringList(actionDetails);
				outputString=String.format(formattedString, stateString,actionString);
				bWriter.write(outputString);
				bWriter.newLine();
			}
			
		}
		catch (Exception e)
		{
			java.io.StringWriter traceText = new StringWriter();
			java.io.PrintWriter pWriter = new PrintWriter(traceText,true);
			e.printStackTrace(pWriter);
			pWriter.close();
			throw new RuntimeException(traceText.toString());
		}
		finally
		{
			try
			{
				if (bWriter!=null)
					bWriter.close();
			}
			catch (Exception e2)
			{
				
			}
		}
	}
	

	
	public LinkedList<WeightedValue<Integer>> SelectActionDistribution(int[] state, WeightedValue<Integer> skewValue, InferenceStrategy method)
	{
		LinkedList<Integer> mappedIndices =MapIndex(state,method);
		if (mappedIndices==null)
			return null;
		LinkedList<WeightedValue<Integer>> actionDistrib=GetCumulativeActionDistribution(mappedIndices);
		
		WeightedValue<Integer> priorValue = AITools.FindValueInList(actionDistrib, skewValue);
		
		if (priorValue==null)
		{
			return actionDistrib;
			
		}
		else
		{
			WeightedValue<Integer> overideValue = new WeightedValue<Integer>(skewValue.GetValue(), skewValue.GetWeight()*priorValue.GetWeight());
			LinkedList<WeightedValue<Integer>> overideList = new LinkedList<WeightedValue<Integer>>();
			overideList.add(overideValue);
			return AITools.ListOveride(actionDistrib, overideList);
		}
	}
	
	public LinkedList<WeightedValue<Integer>> SelectActionDistribution(int[] state, InferenceStrategy method)
	{
		LinkedList<Integer> mappedIndices =MapIndex(state,method);
		if (mappedIndices==null)
			return null;
		LinkedList<WeightedValue<Integer>> actionDistrib=GetCumulativeActionDistribution(mappedIndices);
		int[][] out = null;
		out = AITools.DistribViewer(actionDistrib);
		return actionDistrib;
		
	}
	
	
	/**
	 * Changes the frequency in which a given action was executed in a given state in
	 * the behavioral map
	 * 
	 * @param stateIndex
	 * @param actionIndex
	 * @param actionDelta
	 */
	public void IncrementActionDistribution(int stateIndex,int actionIndex,int actionDelta)
	{
		int[] state = AITools.ReverseMapIndexToVector(stateIndex, j_StateComponentRadii);
		for (int j=0;j<j_StateComponentRadii.length;j++)
		{
			j_ValueDistribution[j].addValue(state[j]);
		}
		
		LinkedList<WeightedValue<Integer>> actionDistrib;
		int index=GetAllocationIndex(stateIndex, true);
		if (index==j_AllocationBufferSize)
		{
			// No free indices
			// TODO: need to do something smarter here, like reusing an existing index (probably setting to
			// the last allocated value
			return;
		}
		else 
		{
			actionDistrib=j_StateActionDistribution[index];
			if (actionDistrib==null)
			{
				actionDistrib = new LinkedList<WeightedValue<Integer>>();
				j_StateActionDistribution[index]=actionDistrib;
				if (j_AllocationBufferSize>j_NumStatesAllocated)
					j_AllocatedStateList[j_NumStatesAllocated++]=stateIndex;
				else
				{
					// TODO: Do something
				}
				
			}
			
			for (WeightedValue<Integer> action:actionDistrib)
			{
				if (action.GetValue().intValue()==actionIndex)
				{
					action.UpdateValue(actionDelta);
					return;
				}
			}
			//actionDistrib.add(new WeightedValue<Integer>(actionIndex,actionDelta));
			
		}
		actionDistrib.add(new WeightedValue<Integer>(actionIndex,actionDelta));
	}
	
	
	
	
	
	
	private LinkedList<Integer> MapIndexFromEntropy(int[] stateComponents, double[] entropyFractionVector, int variantLength)
	{
		LinkedList<Integer> mappedIndices=null;
		int stateLength = stateComponents.length;
		int[] effectiveState = new int[stateLength];
		int[] variantIndexMapping = new int[variantLength]; // first is index, value
		
		// variantIndexMapping: N one -> {i:N|entropyFractionVector[i]!=0}
		// length(variantIndexMapping)=variantLength
		int vCounter=0;
		int[] variantPermutation = new int[variantLength];
		int[][] searchRange = new int[variantLength][2];
		// searchRange: {i:range(variantIndexMapping) * (i, {x|x<=comp(i)-f(i)*rad(i)<=comp(i)+f(i)*rad(i)})}
		
		int minValue;
		int maxValue;
		
		for (int i=0;i<stateLength;i++)
			if (entropyFractionVector[i]!=0)
			{
				variantIndexMapping[vCounter]=i;
				if (entropyFractionVector[i]==1.0)
				{
					minValue=0;
					maxValue=j_StateComponentRadii[i]-1;
				}
				else
				{
					minValue=(int)Math.max(0, stateComponents[i]-j_StateComponentRadii[i]*entropyFractionVector[i]);
					maxValue=(int)Math.min(j_StateComponentRadii[i]-1, stateComponents[i]+j_StateComponentRadii[i]*entropyFractionVector[i]);	
				}
				
				searchRange[vCounter][0]=minValue;
				searchRange[vCounter][1]=maxValue;
				variantPermutation[vCounter]=minValue;
				vCounter++;
				effectiveState[i]=minValue;
			}
			else
				effectiveState[i]=stateComponents[i];
		int mappedIndex;
		// Now iterate over variant states
		
		while (variantPermutation[variantLength-1]<searchRange[variantLength-1][1])
		{
			for (int variantIndex=0;variantIndex<variantLength;variantIndex++)
			{
				if (variantPermutation[variantIndex]<searchRange[variantIndex][1])
				{
					effectiveState[variantIndexMapping[variantIndex]]=variantPermutation[variantIndex];
					variantPermutation[variantIndex]++;
					break;
				}
				else
				{
					effectiveState[variantIndexMapping[variantIndex]]=variantPermutation[variantIndex];
					variantPermutation[variantIndex]=searchRange[variantIndex][0];
				}
			}
			
			mappedIndex =  GetAllocationIndex(effectiveState, false);
			if (j_AllocationBufferSize!=mappedIndex)
			{
				if (mappedIndices==null)
					mappedIndices = new LinkedList<Integer>();
				mappedIndices.add(new Integer(mappedIndex));
			}
			
		}

		
		return mappedIndices;
	}
	
	private int GetStateEntropy(int[] stateComponents,double[] entropyFractionVector)
	{
		int entropy=1;
		int minValue, maxValue;
		for (int i=0;i<entropyFractionVector.length;i++)
		{
			if (entropyFractionVector[i]!=0)
			{
				if (entropyFractionVector[i]==1.0)
					entropy*=j_StateComponentRadii[i];
				else
				{
					minValue=(int)Math.max(0, stateComponents[i]-j_StateComponentRadii[i]*entropyFractionVector[i]);
					maxValue=(int)Math.min(j_StateComponentRadii[i]-1, stateComponents[i]+j_StateComponentRadii[i]*entropyFractionVector[i]);
					entropy*=maxValue-minValue+1;
				}
			}
		}
		return entropy;
	}

	
	private boolean InNeighborhoodP(int[] baseState, int[] compState, double[] radiiVector)
	{
		for (int i=0;i<baseState.length;i++)
		{
			if ((radiiVector[i]==1.0)||(baseState[i]==-1))
				continue;
			else
				if (Math.abs(baseState[i]-compState[i])>radiiVector[i]*j_StateComponentRadii[i])
					return false;
		}
		return true;
	}
	
	
	private double distanceThreshold(int[] baseVector, int[] compVector)
	{
		double sum=0, delta;
		for (int i=0;i<baseVector.length;i++)
		{
			
			if (j_ValueDistribution[i]!=null)
			{
				if (j_ValueDistribution[i].distanceWeight(baseVector[i], compVector[i])>0)
				{
					delta=Math.abs(baseVector[i]-compVector[i]);
					if (delta>j_BaseInferentialFreedom[i]*j_StateComponentRadii[i])
					{
						return -1;
					}
					else
						sum+=delta*delta;
				}
				
			}
			
		}
		return Math.sqrt(sum);
	}
	
	private LinkedList<WeightedValue<Integer>> GetCumulativeActionDistribution(LinkedList<Integer> mappedIndices)
	{
		int index;
		boolean first=true;
		LinkedList<WeightedValue<Integer>> effectiveDistrib=null,currentDistrib=null;
		int allocIndex;
		for (Integer mappedIndex:mappedIndices)
		{
			index=mappedIndex.intValue();
			//allocIndex=AITools.HashMapDiscreteVector(index, j_AllocatedStateSet,false);
			if (first)
			{
				effectiveDistrib=(LinkedList<WeightedValue<Integer>>)j_StateActionDistribution[index];
				first=false;
			}
			else
			{
				
				currentDistrib=(LinkedList<WeightedValue<Integer>>)j_StateActionDistribution[index];
				effectiveDistrib=AITools.MergeLists(currentDistrib, effectiveDistrib);
			}
		}
		return effectiveDistrib;
	}
	

	
	private double[] GetEffectiveInferentialFreedom(int[] baseData, InferenceStrategy strategy)
	{
		double[] freedom = new double[baseData.length];
		switch (strategy.j_InferenceMethod)
		{
			case BAYESIAN:
				for (int i=0;i<baseData.length;i++)
				{
					if (baseData[i]!=-1)
						freedom[i]=0;
					else
						freedom[i]=1;
				}
				return freedom;
			case NEIGHBORHOOD:
				double fract;
				for (int i=0;i<baseData.length;i++)
				{
					if (strategy.j_InferenceFreedom==null)
						fract=strategy.j_InferenceFreedomFraction;
					else
						fract = strategy.j_InferenceFreedom[i];
					if (baseData[i]!=-1)
						//freedom[i]=fract*j_BaseInferentialFreedom[i]; // TODO: revisit this.  Ignoring for now
						freedom[i]=j_BaseInferentialFreedom[i];
					else
						freedom[i]=1*j_BaseInferentialFreedom[i]; // TODO: Revisit this
				}
				return freedom;
			default:				
				for (int i=0;i<baseData.length;i++)
				{
					freedom[i]=0;
				}
				return freedom;		
		}
		
	}
	
	
	
	private LinkedList<Integer> MapIndexFromAllocated(int[] stateComponents, double[] entropyFractionVector)
	{
		int[] allocatedState;
		LinkedList<Integer> mappedIndices=null;
		for (int i=0;i<j_NumStatesAllocated;i++)
		{
			allocatedState = AITools.ReverseMapIndexToVector(j_AllocatedStateList[i], j_StateComponentRadii);
			if (InNeighborhoodP(stateComponents, allocatedState, entropyFractionVector))
			{
				if (mappedIndices==null)
					mappedIndices = new LinkedList<Integer>();
				mappedIndices.add(new Integer(GetAllocationIndex(j_AllocatedStateList[i], false)));
			}
		}
		return mappedIndices;
	}
	
	// TODO: Test this before extending it to other methods
	private LinkedList<Integer> MapIndexFromAllocated(int[] stateComponents, double mismatchTerm)
	{
		int[] allocatedState;
		LinkedList<WeightedValue<Integer>> distances = new LinkedList<WeightedValue<Integer>>();
		LinkedList<Integer> mappedIndices = null;
		double dist, minDist=0;
		boolean first=true;
		int[][] baseDistrib=null, newDistrib=null,winnerDistrib=null;
		LinkedList<WeightedValue<Integer>> modifiedDistribution=null; // Not currently used except as a flag
		for (int i=0;i<j_NumStatesAllocated;i++)
		{
			 
			allocatedState = AITools.ReverseMapIndexToVector(j_AllocatedStateList[i], j_StateComponentRadii);
			dist=distanceThreshold(allocatedState,stateComponents);
			baseDistrib = AITools.DistribViewer((LinkedList<WeightedValue<Integer>>)j_StateActionDistribution[GetAllocationIndex(j_AllocatedStateList[i], false)]);
			if (j_ExtinctionPolicy!=null)
				modifiedDistribution = j_ExtinctionPolicy.TransformPoint((LinkedList<WeightedValue<Integer>>)j_StateActionDistribution[GetAllocationIndex(j_AllocatedStateList[i], false)]);
			if ((dist>=0)&&(j_ExtinctionPolicy==null|| modifiedDistribution!=null))
			{
				newDistrib = AITools.DistribViewer(modifiedDistribution);
				if (first||dist<=minDist)
				{
					if (dist<minDist)
					{
						distances = new LinkedList<WeightedValue<Integer>>();
						
					}
					distances.add(new WeightedValue<Integer>(GetAllocationIndex(j_AllocatedStateList[i], false), dist));
					minDist=dist;
					first=false;
					
				}
				
			}
		}
		
		if (distances.size()>0)
		{
			ProbabilisticTrainingAgent.j_InferredDecision=true;
			int[] winner=null;
			mappedIndices = new LinkedList<Integer>();
			WeightedValue<Integer> v = AITools.ChooseRandom(distances);
			mappedIndices.add(v.GetValue());
			winner=AITools.DistribViewer(mappedIndices);
			winnerDistrib=AITools.DistribViewer((LinkedList<WeightedValue<Integer>>)j_StateActionDistribution[winner[0]]);

		}
		
		return mappedIndices;
	}
	/***************************************
	 * BEGINNING OF SERIALIZATION-RELATED FIELDS AND METHODS
	 ************************************** 
	 */
	private enum LoadState
	{
		
		READING_RADII_SIZE,
		READING_RADII,
		READING_ALLOCATION_SET,
		READING_DISTRIBUTION_LENGTH,
		READING_DISTRIBUTION,
		EXIT
	}
	
	private enum SaveState
	{
		WRITING_RADII_SIZE,
		WRITING_RADII,
		WRITING_ALLOCATION_SET,
		WRITING_DISTRIBUTION_LENGTH,
		WRITING_DISTRIBUTION,
		EXIT
	}
	
	
	private LoadState j_CurrentState;
	private String j_UniqueName;
	private SerializerAdapter j_SerializerHelper;
	
	public void SetName(String uniqueName)
	{
		j_UniqueName=uniqueName;
		j_SerializerHelper.SetUniqueName(uniqueName);
	}
	
	public void PrepareSerialization()
	{
		j_CurrentState=LoadState.READING_RADII_SIZE;
	}
	
	public void Load(BufferedReader reader)
	{
		j_SerializerHelper.Load(reader);
	}
	
	
	public void Load(String fileFullName)
	{
		j_SerializerHelper.Load(fileFullName);
		
	}
	

	
	public void Save(String fileFullName)
	{
		j_SerializerHelper.Save(fileFullName);
	}
	
	
	public  void Save(BufferedWriter writer)
	{
		j_SerializerHelper.Save(writer);
	}
	
	
	
	public void SaveData(BufferedWriter writer)
	{
		SaveState currentSaveState = SaveState.WRITING_RADII_SIZE;
		String data;
		boolean first=true;
		
		try
		{
			while (currentSaveState!=SaveState.EXIT)
			{
				switch (currentSaveState)
				{
					case WRITING_RADII_SIZE:
						if ((j_StateComponentRadii==null)||(j_StateComponentRadii.length==0))
						{
							writer.write(":0");
							writer.newLine();
							currentSaveState=SaveState.EXIT;
						}
						else
						{
							data = String.format(":%1$s",j_StateComponentRadii.length );
							writer.write(data);
							writer.newLine();
							currentSaveState=SaveState.WRITING_RADII;
						}
						break;
					case WRITING_RADII:
						// Write each dimension
						for (int i=0;i<j_StateComponentRadii.length;i++)
						{
							data = String.format(":%1$s",j_StateComponentRadii[i] );
							writer.write(data);
							writer.newLine();
						}
						currentSaveState=SaveState.WRITING_ALLOCATION_SET;
						break;
					case WRITING_ALLOCATION_SET:
						String baseFormat="%1$s|%2$s";
						String additionalFormat=",%1$s|%2$s";
						StringBuilder sBuilder = new  StringBuilder(String.format(baseFormat, j_AllocatedStateSet[0][0], j_AllocatedStateSet[0][1]));
						
						for (int i=1;i<j_AllocatedStateSet.length;i++)
						{
							sBuilder.append(String.format(additionalFormat, j_AllocatedStateSet[i][0], j_AllocatedStateSet[i][1]));
						}
						StaticTools.WriteRawData(writer, sBuilder.toString());
						currentSaveState=SaveState.WRITING_DISTRIBUTION_LENGTH;
						break;
					case WRITING_DISTRIBUTION_LENGTH:
						if ((j_StateActionDistribution==null)||(j_StateActionDistribution.length==0))
						{
							writer.write(":0");
							writer.newLine();
							currentSaveState=SaveState.EXIT;
						}
						else
						{
							writer.write(":"+j_StateActionDistribution.length);
							writer.newLine();
							currentSaveState=SaveState.WRITING_DISTRIBUTION;
						}
						break;
					case WRITING_DISTRIBUTION:
						for (LinkedList<WeightedValue<Integer>> listData:j_StateActionDistribution)
						{
							if ((listData==null)||(listData.size()==0))
							{
								writer.write(":");
								writer.newLine();
							}
							else
							{
								writer.write(":");
								first=true;
								for (WeightedValue<Integer> value:listData)
								{
									if (first)
									{
										first=false;
										data=String.format("%1$s|%2$s", value.GetValue().intValue(), (int)value.GetWeight());
									}
									else
										data=String.format(",%1$s|%2$s", value.GetValue().intValue(), (int)value.GetWeight());
									writer.write(data);
								}
								writer.newLine();
							}
						}
						currentSaveState=SaveState.EXIT;
						break;
				}
				
			}
		}
		catch (Exception e)
		{
			java.io.StringWriter traceText = new java.io.StringWriter();
			java.io.PrintWriter pWriter = new java.io.PrintWriter(traceText,true);
			e.printStackTrace(pWriter);
			pWriter.close();
			throw new RuntimeException(traceText.toString());
		}


	}
	
	public void AddData(String dataLine)
	{
		String[] components;
		switch (j_CurrentState)
		{
			case READING_RADII_SIZE:
				int size=Integer.parseInt(dataLine);
				j_RadiiLength=size;
				if (size==0)
					j_CurrentState=LoadState.EXIT;
				else
				{
					j_StateComponentRadii=new int[size];
					j_ValueDistribution = new StateValueDistribution[size];
					j_CurrentState=LoadState.READING_RADII;
					j_RadiiIndex=0;
				}
				break;
			case READING_RADII:
				j_StateComponentRadii[j_RadiiIndex]=Integer.parseInt(dataLine);
				j_RadiiIndex++;
				if (j_RadiiIndex==j_RadiiLength)
					j_CurrentState=LoadState.READING_ALLOCATION_SET;
				else
					j_CurrentState=LoadState.READING_RADII;
				break;
			case READING_ALLOCATION_SET:
				components = StaticTools.SplitData(dataLine,",");
				String[] pair;
				j_AllocationBufferSize = components.length;
				j_AllocatedStateSet = new int[j_AllocationBufferSize][2];
				j_NumStatesAllocated=0;
				j_AllocatedStateList = new int[j_AllocationBufferSize];
				for (int i=0;i<j_AllocationBufferSize;i++)
				{
					pair = StaticTools.SplitData(components[i],"|");
					j_AllocatedStateSet[i][0]=Integer.parseInt(pair[0]);
					j_AllocatedStateSet[i][1]=Integer.parseInt(pair[1]);
					if (j_AllocatedStateSet[i][0]!=-1)
						j_AllocatedStateList[j_NumStatesAllocated++]=j_AllocatedStateSet[i][0];
				}
				for (int i=j_NumStatesAllocated;i<j_AllocationBufferSize;i++)
					j_AllocatedStateList[i]=-1;
				j_CurrentState=LoadState.READING_DISTRIBUTION_LENGTH;
				break;
			case READING_DISTRIBUTION_LENGTH:
				j_DistributionLength=Integer.parseInt(dataLine);
				if (j_DistributionLength==0)
					j_CurrentState=LoadState.EXIT;
				else
				{
					j_StateActionDistribution=new LinkedList[j_DistributionLength];
					
					j_CurrentState=LoadState.READING_DISTRIBUTION;
					j_DistributionIndex=0;
				}
				break;
			case READING_DISTRIBUTION:
				if (dataLine.length()>0)
				{
					components = dataLine.split(",");
					String[] valueWeightPair;
					j_StateActionDistribution[j_DistributionIndex]= new LinkedList();
					int value;
					int weight;
					for (String keyValueString:components)
					{
						valueWeightPair=keyValueString.split("\\|");
						value=Integer.parseInt(valueWeightPair[0]);
						weight=Integer.parseInt(valueWeightPair[1]);
						j_StateActionDistribution[j_DistributionIndex].add(new WeightedValue<Integer>(new Integer(value),weight));
						
					}
				}
				else
				{
					j_StateActionDistribution[j_DistributionIndex]=null;
				}
				j_DistributionIndex++;
				if (j_DistributionIndex==j_DistributionLength)
					j_CurrentState=LoadState.EXIT;
				else
					j_CurrentState=LoadState.READING_DISTRIBUTION;
				break;
				
		}
	}
	
	
	public void AddData(Serializer inner, String instanceId)
	{
		
	}
	
	public String GetTypeName()
	{
		return "BehaviorDistribution";
	}
	
}
